package com.zycfc.zsf.boot.util.bean;

import java.util.*;
import java.lang.reflect.*;

public abstract class Classes
{
    private static final HashMap<String, Class<?>> PRIMITIVE_CLASS_MAP;
    private static final HashMap<Class<?>, String> CLASS_TO_SIGNATURE_MAP;
    
    public static Set<Class<?>> getAllTypes(final Class<?> clazz) {
        final Set<Class<?>> r = new LinkedHashSet<Class<?>>();
        final Set<Class<?>> set = getClasses(clazz);
        r.addAll(set);
        for (final Class<?> c : set) {
            r.addAll(getInterfaces(c));
        }
        return r;
    }
    
    public static Set<Class<?>> getAllInterfaces(final Class<?> clazz) {
        final Set<Class<?>> set = getClasses(clazz);
        final Set<Class<?>> r = new LinkedHashSet<Class<?>>();
        for (final Class<?> c : set) {
            r.addAll(getInterfaces(c));
        }
        return r;
    }
    
    public static Class<?>[] getAllInterfacesAsArray(final Class<?> clazz) {
        final Set<Class<?>> set = getAllInterfaces(clazz);
        int i = 0;
        final Class<?>[] r = (Class<?>[])new Class[set.size()];
        for (final Class<?> c : set) {
            r[i++] = c;
        }
        return r;
    }
    
    public static Class<?> primitiveToClass(final Class<?> clazz) {
        return clazz.isPrimitive() ? typeNameToClass(clazz.getName()) : clazz;
    }
    
    public static Class<?> loadClass(final String className, final ClassLoader classLoader) throws ClassNotFoundException {
        if (className == null) {
            throw new IllegalArgumentException("parameter className can not be null");
        }
        if (classLoader == null) {
            throw new IllegalArgumentException("parameter classLoader can not be null");
        }
        try {
            return classLoader.loadClass(className);
        }
        catch (ClassNotFoundException ex) {
            final Class<?> resolvedClass = Classes.PRIMITIVE_CLASS_MAP.get(className);
            if (resolvedClass != null) {
                return resolvedClass;
            }
            if (className.endsWith(";") && className.startsWith("L")) {
                final String typeName = className.substring(1, className.length() - 1);
                return classLoader.loadClass(typeName);
            }
            if (className.charAt(0) == '[') {
                int count = 0;
                for (int nameLen = className.length(); count < nameLen && className.charAt(count) == '['; ++count) {}
                final String arrayTypeName = className.substring(count, className.length());
                final Class<?> arrayType = loadClass(arrayTypeName, classLoader);
                return getArrayClass(arrayType, count);
            }
            if (className.endsWith("[]")) {
                int count = 0;
                int position;
                for (position = className.length(); position > 1 && className.substring(position - 2, position).equals("[]"); position -= 2) {
                    ++count;
                }
                final String typeName2 = className.substring(0, position);
                final Class<?> arrayType = loadClass(typeName2, classLoader);
                return getArrayClass(arrayType, count);
            }
            throw new ClassNotFoundException("could not load class: " + className + " from classloader: " + classLoader);
        }
    }
    
    private static Class<?> typeNameToClass(String typeName) {
        typeName = typeName.intern();
        if (typeName == "boolean") {
            return Boolean.class;
        }
        if (typeName == "byte") {
            return Byte.class;
        }
        if (typeName == "char") {
            return Character.class;
        }
        if (typeName == "short") {
            return Short.class;
        }
        if (typeName == "int") {
            return Integer.class;
        }
        if (typeName == "long") {
            return Long.class;
        }
        if (typeName == "float") {
            return Float.class;
        }
        if (typeName == "double") {
            return Double.class;
        }
        if (typeName == "void") {
            return Void.class;
        }
        return null;
    }
    
    private static Set<Class<?>> getClasses(final Class<?> clazz) {
        final Set<Class<?>> r = new LinkedHashSet<Class<?>>();
        r.add(clazz);
        for (Class<?> superClass = clazz.getSuperclass(); superClass != null; superClass = superClass.getSuperclass()) {
            r.add(superClass);
        }
        return r;
    }
    
    private static Set<Class<?>> getInterfaces(final Class<?> clazz) {
        final Set<Class<?>> r = new LinkedHashSet<Class<?>>();
        final LinkedList<Class<?>> stack = new LinkedList<Class<?>>();
        stack.addAll(Arrays.asList(clazz.getInterfaces()));
        while (!stack.isEmpty()) {
            final Class<?> intf = stack.removeFirst();
            if (!r.contains(intf)) {
                r.add(intf);
                stack.addAll(Arrays.asList(intf.getInterfaces()));
            }
        }
        return r;
    }
    
    private static Class<?> getArrayClass(final Class<?> type, final int dimension) {
        final int[] dimensions = new int[dimension];
        return Array.newInstance(type, dimensions).getClass();
    }
    
    private static String getClassName(Class<?> type) {
        final StringBuilder name = new StringBuilder();
        while (type.isArray()) {
            name.append('[');
            type = type.getComponentType();
        }
        if (type.isPrimitive()) {
            name.append(Classes.CLASS_TO_SIGNATURE_MAP.get(type));
        }
        else {
            name.append('L');
            name.append(type.getName());
            name.append(';');
        }
        return name.toString();
    }
    
    static {
        PRIMITIVE_CLASS_MAP = new HashMap<String, Class<?>>();
        CLASS_TO_SIGNATURE_MAP = new HashMap<Class<?>, String>();
        Classes.PRIMITIVE_CLASS_MAP.put("boolean", Boolean.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("Z", Boolean.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("byte", Byte.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("B", Byte.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("char", Character.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("C", Character.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("short", Short.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("S", Short.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("int", Integer.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("I", Integer.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("long", Long.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("J", Long.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("float", Float.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("F", Float.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("double", Double.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("D", Double.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("void", Void.TYPE);
        Classes.PRIMITIVE_CLASS_MAP.put("V", Void.TYPE);
        Classes.CLASS_TO_SIGNATURE_MAP.put(Boolean.TYPE, "Z");
        Classes.CLASS_TO_SIGNATURE_MAP.put(Byte.TYPE, "B");
        Classes.CLASS_TO_SIGNATURE_MAP.put(Character.TYPE, "C");
        Classes.CLASS_TO_SIGNATURE_MAP.put(Short.TYPE, "S");
        Classes.CLASS_TO_SIGNATURE_MAP.put(Integer.TYPE, "I");
        Classes.CLASS_TO_SIGNATURE_MAP.put(Long.TYPE, "J");
        Classes.CLASS_TO_SIGNATURE_MAP.put(Float.TYPE, "F");
        Classes.CLASS_TO_SIGNATURE_MAP.put(Double.TYPE, "D");
        Classes.CLASS_TO_SIGNATURE_MAP.put(Void.TYPE, "V");
    }
}
